#ifndef _IOMANAGER_H_
#define _IOMANAGER_H_

#include <vector>
#include <string>
#include <limits>

using namespace std;

#define ERROR_THRESHOLD 0.5
#define SMOOTH_THRESHOLD 10
#define PI 3.14159265

struct Vertex 
{
	Vertex()
	{
	}
	Vertex(double _x, double _y, double _z)
	{
		x = _x;
		y = _y;
		z = _z;
        unfoldAngle = 0;
        isLocalMax = false;
        isLocalMin = false;
		commonListIndex = -1;
	}
	double x;
	double y;
	double z;

    bool isLocalMax;
    bool isLocalMin;
	int commonListIndex; //maintain to simplify mesh

	double functionValue;
    double unfoldAngle;
    vector<int> listNeighbor;
	
	bool operator==(Vertex *e)
	{
		if (this->x == e->x && this->y == e->y && this->z == e->z)
			return true;
		return false;
	}
	//set function value
	void setVertexFunctionValue(double _val)
	{
		functionValue = _val;
	}
	//set coordinate
	void setVertexCoord(double _x, double _y, double _z)
	{
		this->x = _x;
		this->y = _y;
		this->z = _z;
	}
	void setCommonIndex (int _i)
	{
		this->commonListIndex = _i;
	}
};

struct Edge
{
    Edge(int _v1, int _v2)
    {
        vertex1Index = _v1;
        vertex2Index = _v2;
        lengthSquare = -1;
    }
	bool operator==(Edge *e)
	{
		if (this->vertex1Index == e->vertex1Index && this->vertex2Index == e->vertex2Index)
			return true;
		return false;
	}
    int vertex1Index;
    int vertex2Index;
    double lengthSquare;
    vector<int> listNeighbor;
};

struct Face
{
	Face(int _num)
	{
		numVertex = _num;
	}
	int numVertex;
	vector<int> listVertexIndex;
	vector<int> listNeighbor;//index of neighbor face
	void addNeighbor(int _fIndex)
	{
		for (int i = 0; i < listNeighbor.size(); ++i)
		{
			if (_fIndex == listNeighbor.at(i))
			{
				return;
			}
		}
		listNeighbor.push_back(_fIndex);
	}
};

struct GraphNode 
{
	GraphNode()
	{
		faceId = -1;
		visited = false;
		offset = 0.0;
	}
	vector<GraphNode*> neighborList;
	int faceId;
	bool visited;
	//unfold coord
	Vertex unfoldV1, unfoldV2, unfoldV3;
	double offset;
	void resetVisitedState()
	{
		visited = false;
	}
};
struct GraphEdge 
{
	GraphEdge()
	{
		n1 = n2 = -1;
	}
	int n1;
	int n2;
	void setEdge(int _n1, int _n2)
	{
		n1 = _n1;
		n2 = _n2;
	}
	bool equals(GraphEdge * e)
	{
		if ( (this->n1 == e->n1 && this->n2 == e->n2)
			||(this->n1 == e->n2 && this->n2 == e->n1))
		{
			return true;
		} 
		return false;
	}
};

class IOManager
{
private:
    vector<Vertex> l_VertexDataArray;
	vector<Face> l_FaceDataArray;
    vector<int> l_LocalMaxIndex;
    vector<int> l_LocalMinIndex;
	vector<int> l_LocalSaddleIndex;
    vector<Edge> l_EdgeDataArray;

	vector<vector<int>> l_TraceLineList;

	vector<vector<int>> l_commonCoordList;//List of list of vertices that will be simplified

	vector<GraphNode> faceGraph;//face graph for unfolding
	vector<GraphEdge> ToDeleteEdgeList;//list of edge to be delete

public:
    //constructor
    IOManager();
    //read from .off model and put in data array
    void ReadDataToDatabase(string _fileName);
	//return pointer to Vertex Array
	vector<Vertex> * GetVertexDataArray() {return &l_VertexDataArray;}
	//return pointer to Face Array
    vector<Face> * GetFaceDataArray() {return &l_FaceDataArray;}
	//return pointer to max min array
    vector<int> * GetLocalMaxList() {return &l_LocalMaxIndex;}
    vector<int> * GetLocalMinList() {return &l_LocalMinIndex;}
	vector<int> * GetLocalSaddleList() {return &l_LocalSaddleIndex;}
	vector<vector<int>> * GetTraceLineList() {return &l_TraceLineList;}
	vector<GraphNode> * GetGraph() {return &faceGraph;}


	void CalculateFunctionValue();
    void CalculateUnfoldAngle();
    void SetNeighborList();
    void CalculateLocalMaxMin();
	void CalculateLocalSaddle();
    void CalculateTraceList();

	void SimplifyMesh();
	void SmoothenSurface();
	void SetFaceNeighborList();

	//Graph for unfolding process
	void BuildGraphFromMesh();
	void BreakGraphToTree();
	void CalculateUnfoldCoord();
	//Add trace edge to ToDeletedList so we can segment the graph
	void SegmentGraph();

private:
    bool CheckDuplicate(vector<int> * _list, int _key);
    double GetLength(Vertex a, Vertex b);
    void GetEdgeDataFromFace(Face* f);
    Edge * FindEdgeByStartPoint(int _vIndex);
    Edge * FindEdge(int _vStart, int _vEnd);
	int GetMaxNeighbor(int _vIndex);//return index of neighbor that has max function val
	int GetMinNeighbor(int _vIndex);//return index of neighbor that has min function val
	int GetNumCommonNeighbor(int _v1Index, int _v2Index);
	vector<int> GetCommonNeighborWithExclusion(int _v1Index, int _v2Index, int _excludeIndex);//not the exclude pts
    void TraceLine(int previous, vector<int> & traceList, double val1);
    double CalculateAngleBetween(Vertex& commonVertex, Edge& e1, Edge& e2);
	double * CalculateAverageCoord(vector<int> * list);
	int FindFace(int _v1, int _v2, int _v3);

	//reset visited state of the graph
	void ResetGraph();
	void ProcessNode(GraphNode * _n, GraphNode * _parent);//recursive
	bool CheckValidTree(GraphNode * _n, GraphNode * _parent);//VALIDATION
	bool CheckExistedEdge(GraphEdge * e);
	void DeleteGraphEdge();// break all loop in graph
	
	void RecursivelyUnfold(GraphNode * n, GraphNode * parent, double offset);//recursive function for each graph node

	//given 3 pts, 2 have unfold Coord, calculate the unfold coord of the 3rd
	//parent U3 is the un-common pts on unfold plane, to compare distance
	void RebuildTriangle(Vertex * unfoldV1, Vertex * unfoldV2, Vertex * unfoldV3, 
		Vertex * parentU1, Vertex * parentU2, Vertex * parentU3,
		Vertex * origN1, Vertex * origN2,Vertex * origN3,
		double offset, GraphNode *n);
};

#endif _IOMANAGER_H_